<template>
  <Tooltip placement="top">
    <template #title>
      <span>{{ t('component.table.settingColumn') }}</span>
    </template>
    <Popover
      placement="bottomLeft"
      trigger="click"
      @open-change="handleVisibleChange"
      :overlayClassName="`${prefixCls}__column-list`"
      :getPopupContainer="getPopupContainer"
    >
      <template #title>
        <div :class="`${prefixCls}__popover-title`">
          <Checkbox
            :indeterminate="indeterminate"
            v-model:checked="state.checkAll"
            @change="onCheckAllChange"
          >
            {{ t('component.table.settingColumnShow') }}
          </Checkbox>

          <Checkbox v-model:checked="checkIndex" @change="handleIndexCheckChange">
            {{ t('component.table.settingIndexColumnShow') }}
          </Checkbox>

          <Checkbox
            v-model:checked="checkSelect"
            @change="handleSelectCheckChange"
            :disabled="!defaultRowSelection"
          >
            {{ t('component.table.settingSelectColumnShow') }}
          </Checkbox>

          <a-button size="small" type="link" @click="reset">
            {{ t('common.resetText') }}
          </a-button>
        </div>
      </template>

      <template #content>
        <ScrollContainer>
          <Checkbox.Group v-model:value="state.checkedList" @change="onChange" ref="columnListRef">
            <template v-for="item in plainOptions" :key="item.value">
              <div :class="`${prefixCls}__check-item`" v-if="!('ifShow' in item && !item.ifShow)">
                <DragOutlined class="table-column-drag-icon" />
                <Checkbox :value="item.value">
                  {{ item.label }}
                </Checkbox>

                <Tooltip
                  placement="bottomLeft"
                  :mouseLeaveDelay="0.4"
                  :getPopupContainer="getPopupContainer"
                >
                  <template #title>
                    {{ t('component.table.settingFixedLeft') }}
                  </template>
                  <Icon
                    icon="line-md:arrow-align-left"
                    :class="[
                      `${prefixCls}__fixed-left`,
                      {
                        active: item.fixed === 'left',
                        disabled: !state.checkedList.includes(item.value),
                      },
                    ]"
                    @click="handleColumnFixed(item, 'left')"
                  />
                </Tooltip>
                <Divider type="vertical" />
                <Tooltip
                  placement="bottomLeft"
                  :mouseLeaveDelay="0.4"
                  :getPopupContainer="getPopupContainer"
                >
                  <template #title>
                    {{ t('component.table.settingFixedRight') }}
                  </template>
                  <Icon
                    icon="line-md:arrow-align-left"
                    :class="[
                      `${prefixCls}__fixed-right`,
                      {
                        active: item.fixed === 'right',
                        disabled: !state.checkedList.includes(item.value),
                      },
                    ]"
                    @click="handleColumnFixed(item, 'right')"
                  />
                </Tooltip>
              </div>
            </template>
          </Checkbox.Group>
        </ScrollContainer>
      </template>
      <SettingOutlined />
    </Popover>
  </Tooltip>
</template>
<script lang="ts" setup>
  import type { BasicColumn, BasicTableProps, ColumnChangeParam } from '../../types/table';
  import { ref, reactive, watchEffect, nextTick, unref, computed, useAttrs } from 'vue';
  import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue';
  import type {
    CheckboxChangeEvent,
    CheckboxValueType,
  } from 'ant-design-vue/lib/checkbox/interface';
  import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue';
  import Icon from '@/components/Icon/Icon.vue';
  import { ScrollContainer } from '@/components/Container';
  import { useI18n } from '@/hooks/web/useI18n';
  import { useTableContext } from '../../hooks/useTableContext';
  import { useDesign } from '@/hooks/web/useDesign';
  // import { useSortable } from '@/hooks/web/useSortable';
  import { isFunction, isNil } from '@/utils/is';
  import { getPopupContainer as getParentContainer } from '@/utils';
  import { cloneDeep, omit } from 'lodash-es';
  import Sortablejs from 'sortablejs';
  import type Sortable from 'sortablejs';

  interface State {
    checkAll: boolean;
    isInit?: boolean;
    checkedList: string[];
    defaultCheckList: string[];
  }

  interface Options {
    label: string;
    value: string;
    fixed?: boolean | 'left' | 'right';
  }

  defineOptions({ name: 'ColumnSetting' });

  const emit = defineEmits(['columns-change']);

  const attrs = useAttrs();
  const { t } = useI18n();
  const table = useTableContext();

  const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys');
  let inited = false;
  // 是否当前的setColumns触发的
  let isSetColumnsFromThis = false;
  // 是否当前组件触发的setProps
  let isSetPropsFromThis = false;

  const cachePlainOptions = ref<Options[]>([]);
  const plainOptions = ref<Options[] | any>([]);

  const plainSortOptions = ref<Options[]>([]);

  const columnListRef = ref(null);

  const state = reactive<State>({
    checkAll: true,
    checkedList: [],
    defaultCheckList: [],
  });
  /** 缓存初始化props */
  let cacheTableProps: Partial<BasicTableProps<any>> = {};
  const checkIndex = ref(false);
  const checkSelect = ref(false);

  const { prefixCls } = useDesign('basic-column-setting');

  const getValues = computed(() => {
    return unref(table?.getBindValues) || {};
  });

  watchEffect(() => {
    const columns = table.getColumns();
    setTimeout(() => {
      if (isSetColumnsFromThis) {
        isSetColumnsFromThis = false;
      } else if (columns.length) {
        init();
      }
    }, 0);
  });

  watchEffect(() => {
    const values = unref(getValues);
    if (isSetPropsFromThis) {
      isSetPropsFromThis = false;
    } else {
      cacheTableProps = cloneDeep(values);
    }
    checkIndex.value = !!values.showIndexColumn;
    checkSelect.value = !!values.rowSelection;
  });

  function getColumns() {
    const ret: Options[] = [];
    table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => {
      ret.push({
        label: (item.title as string) || (item.customTitle as string),
        value: (item.dataIndex || item.title) as string,
        ...item,
      });
    });
    return ret;
  }

  async function init(isReset = false) {
    // Sortablejs存在bug，不知道在哪个步骤中会向el append了一个childNode，因此这里先清空childNode
    // 有可能复现上述问题的操作：拖拽一个元素，快速的上下移动，最后放到最后的位置中松手
    plainOptions.value = [];
    const columnListEl = unref(columnListRef);
    if (columnListEl && (columnListEl as any).$el) {
      const el = (columnListEl as any).$el as Element;
      Array.from(el.children).forEach((item) => el.removeChild(item));
    }
    await nextTick();
    const columns = isReset ? cloneDeep(cachePlainOptions.value) : getColumns();

    const checkList = table
      .getColumns({ ignoreAction: true, ignoreIndex: true })
      .map((item) => {
        if (item.defaultHidden) {
          return '';
        }
        return item.dataIndex || item.title;
      })
      .filter(Boolean) as string[];
    plainOptions.value = columns;
    plainSortOptions.value = columns;
    // 更新缓存配置
    table.setCacheColumns?.(columns);
    !isReset && (cachePlainOptions.value = cloneDeep(columns));
    state.defaultCheckList = checkList;
    state.checkedList = checkList;
    // 是否列展示全选
    state.checkAll = checkList.length === columns.length;
    inited = false;
    handleVisibleChange();
  }

  // checkAll change
  function onCheckAllChange(e: CheckboxChangeEvent) {
    const checkList = plainSortOptions.value.map((item) => item.value);
    plainSortOptions.value.forEach(
      (item) => ((item as BasicColumn).defaultHidden = !e.target.checked),
    );
    if (e.target.checked) {
      state.checkedList = checkList;
      setColumns(checkList);
    } else {
      state.checkedList = [];
      setColumns([]);
    }
  }

  const indeterminate = computed(() => {
    const len = plainOptions.value.length;
    let checkedLen = state.checkedList.length;
    // unref(checkIndex) && checkedLen--;
    return checkedLen > 0 && checkedLen < len;
  });

  // Trigger when check/uncheck a column
  function onChange(checkedList: CheckboxValueType[]) {
    const len = plainSortOptions.value.length;
    state.checkAll = checkedList.length === len;
    const sortList = unref(plainSortOptions).map((item) => item.value);
    checkedList.sort((prev, next) => {
      return sortList.indexOf(String(prev)) - sortList.indexOf(String(next));
    });
    unref(plainSortOptions).forEach((item) => {
      (item as BasicColumn).defaultHidden = !checkedList.includes(item.value);
    });
    setColumns(checkedList as string[]);
  }

  let sortable: Sortable;
  let sortableOrder: string[] = [];
  // reset columns
  function reset() {
    setColumns(cachePlainOptions.value);
    init(true);
    checkIndex.value = !!cacheTableProps.showIndexColumn;
    checkSelect.value = !!cacheTableProps.rowSelection;
    table.setProps({
      showIndexColumn: checkIndex.value,
      rowSelection: checkSelect.value ? defaultRowSelection : undefined,
    });
    sortable.sort(sortableOrder);
  }

  // Open the pop-up window for drag and drop initialization
  function handleVisibleChange() {
    if (inited) return;
    nextTick(() => {
      const columnListEl = unref(columnListRef);
      if (!columnListEl) return;
      const el = (columnListEl as any).$el;
      if (!el) return;
      // Drag and drop sort
      sortable = Sortablejs.create(unref(el), {
        animation: 500,
        delay: 400,
        delayOnTouchOnly: true,
        handle: '.table-column-drag-icon ',
        onEnd: (evt) => {
          const { oldIndex, newIndex } = evt;
          if (isNil(oldIndex) || isNil(newIndex) || oldIndex === newIndex) {
            return;
          }
          // Sort column
          const columns = cloneDeep(plainSortOptions.value);

          if (oldIndex > newIndex) {
            columns.splice(newIndex, 0, columns[oldIndex]);
            columns.splice(oldIndex + 1, 1);
          } else {
            columns.splice(newIndex + 1, 0, columns[oldIndex]);
            columns.splice(oldIndex, 1);
          }

          plainSortOptions.value = columns;
          setColumns(columns.filter((item) => state.checkedList.includes(item.value)));
        },
      });
      // 记录原始order 序列
      sortableOrder = sortable.toArray();
      inited = true;
    });
  }

  // Control whether the serial number column is displayed
  function handleIndexCheckChange(e: CheckboxChangeEvent) {
    isSetPropsFromThis = true;
    isSetColumnsFromThis = true;
    table.setProps({
      showIndexColumn: e.target.checked,
    });
  }

  // Control whether the check box is displayed
  function handleSelectCheckChange(e: CheckboxChangeEvent) {
    isSetPropsFromThis = true;
    isSetColumnsFromThis = true;
    table.setProps({
      rowSelection: e.target.checked ? defaultRowSelection : undefined,
    });
  }

  function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') {
    if (!state.checkedList.includes(item.dataIndex as string)) return;

    const columns = getColumns().filter((c: BasicColumn) =>
      state.checkedList.includes(c.dataIndex as string),
    ) as BasicColumn[];
    const isFixed = item.fixed === fixed ? false : fixed;
    const index = columns.findIndex((col) => col.dataIndex === item.dataIndex);
    if (index !== -1) {
      columns[index].fixed = isFixed;
    }
    item.fixed = isFixed;

    if (isFixed && !item.width) {
      item.width = 100;
    }
    updateSortOption(item);
    table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed });
    setColumns(columns);
  }

  function setColumns(columns: BasicColumn[] | string[]) {
    isSetPropsFromThis = true;
    isSetColumnsFromThis = true;
    table.setColumns(columns);
    const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => {
      const visible =
        columns.findIndex(
          (c: BasicColumn | string) =>
            c === col.value || (typeof c !== 'string' && c.dataIndex === col.value),
        ) !== -1;
      return { dataIndex: col.value, fixed: col.fixed, visible };
    });

    emit('columns-change', data);
  }

  function getPopupContainer() {
    return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer();
  }

  function updateSortOption(column: BasicColumn) {
    plainSortOptions.value.forEach((item) => {
      if (item.value === column.dataIndex) {
        Object.assign(item, column);
      }
    });
  }
</script>
<style lang="less">
  @prefix-cls: ~'@{namespace}-basic-column-setting';

  .table-column-drag-icon {
    margin: 0 5px;
    cursor: move;
  }

  .@{prefix-cls} {
    &__popover-title {
      display: flex;
      position: relative;
      align-items: center;
      justify-content: space-between;
    }

    &__check-item {
      display: flex;
      align-items: center;
      min-width: 100%;
      padding: 4px 16px 8px 0;

      .ant-checkbox-wrapper {
        width: 100%;

        &:hover {
          color: @primary-color;
        }
      }
    }

    &__fixed-left,
    &__fixed-right {
      color: rgb(0 0 0 / 45%);
      cursor: pointer;

      &.active,
      &:hover {
        color: @primary-color;
      }

      &.disabled {
        color: @disabled-color;
        cursor: not-allowed;
      }
    }

    &__fixed-right {
      transform: rotate(180deg);
    }

    &__column-list {
      svg {
        width: 1em !important;
        height: 1em !important;
      }

      .ant-popover-inner-content {
        // max-height: 360px;
        padding-right: 0;
        padding-left: 0;
        // overflow: auto;
      }

      .ant-checkbox-group {
        display: inline-block;
        width: 100%;
        min-width: 260px;
        // flex-wrap: wrap;
      }

      .scrollbar {
        height: 220px;
      }
    }
  }
</style>
